home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / cool / ge_cool.lha / GE_COOL2.1 / cpp / cpp7.c < prev    next >
C/C++ Source or Header  |  1992-04-13  |  28KB  |  1,003 lines

  1. /*
  2.  
  3.  
  4.  Copyright (C) 1990 Texas Instruments Incorporated.
  5.  
  6.  Permission is granted to any individual or institution to use, copy, modify,
  7.  and distribute this software, provided that this complete copyright and
  8.  permission notice is maintained, intact, in all copies and supporting
  9.  documentation.
  10.  
  11.  Texas Instruments Incorporated provides this software "as is" without
  12.  express or implied warranty.
  13.  
  14.  
  15.  *
  16.  *                C P P 7 . C
  17.  *
  18.  *               Process Internal/External defmacros
  19.  *
  20.  * Edit history
  21.  * 15-Mar-89    LGO    Initial design and implementation.
  22.  * 20-Oct-89    AFM     OS2, XENIX and AIX port.
  23.  * 19-Jan-90    DKM     MVS support
  24.  * 01-Sep-90    MJF     External macros support for OS/2 by using temp file
  25.  * 25-Jun-91    GPD    Remove #elif to make more portable.
  26.  *            Added support for Interactive Unix.
  27.  */
  28.  
  29. #include    <stdio.h>
  30. #include    <ctype.h>
  31. #include    "cppdef.h"
  32.  
  33. #if defined(vms)
  34. #include        <types.h>
  35. #else
  36. #if !defined(SYS_OSVS)
  37. #include        <sys/types.h>
  38. #endif
  39. #endif
  40.  
  41. #if defined(_AIX)
  42. #include        <sys/select.h>
  43. #endif
  44.  
  45. #include    "cpp.h"
  46.  
  47. #if defined(M_XENIX)
  48. #include        <string.h>
  49. #include        <sys/fcntl.h>
  50. #include        <sys/file.h>
  51. #else
  52. #if defined(DOS) || defined(MSDOS)
  53. #include        <string.h>
  54. #include        <fcntl.h>
  55. #else
  56. #if defined(SYS_OSVS)
  57. #include        <string.h>
  58. #include        <fcntl.h>
  59. #else
  60. #if defined(vms)
  61. #include        <file.h>
  62. #else
  63. #if defined(M_INTERACTIVE)
  64. #include    <unistd.h>
  65. #include    <sys/bsdtypes.h>
  66. #include    <sys/fcntl.h>
  67. #endif
  68. #include        <sys/file.h>
  69. #endif
  70. #endif
  71. #endif
  72. #endif
  73.  
  74. #if HOST == SYS_VMS
  75. #define F_OK            0      /* does file exist */
  76. #define X_OK            1      /* is file executable */
  77. #define W_OK            2      /* is file writable */
  78. #define R_OK            4      /* is file readable */
  79. #endif
  80.  
  81. int current_line;            /* line at start of defmacro global */
  82. char current_file[256];            /* file at start of defmacro global */
  83.  
  84. FILEINFO *MacInFile, *MacOutFile;
  85. char* MacOutEnd;
  86.  
  87. struct macroargs {
  88.   char* name;
  89.   char  expanding;        /* Non-zero when args are macro expanded */
  90.   char  recursive;        /* Non-zero when recursive */
  91.   char  delimiter;        /* Delimiter */
  92.   char  conditional;        /* Expand only if this char found after name */
  93.   internal_expander expander;    /* Macro expander or NULL if file */
  94.   char *program;        /* macro program pathname */
  95.   char *args[1];        /* arg list (vector of strings) */
  96. };
  97.  
  98. extern FILEINFO* getfile();
  99.  
  100. /* Getfile without side effects */
  101. FILEINFO*
  102. get_temp_file (bufsize, name)
  103.      int bufsize;
  104.      char* name;
  105. {
  106.   FILEINFO* file = getfile(bufsize, name);
  107.   infile = file->parent;
  108.   file->parent = NULL;
  109.   line = infile->line;
  110.   return(file);
  111. }
  112.  
  113. static FILEINFO*
  114. next_buffer(Buffer)
  115.    FILEINFO* Buffer;
  116. {
  117.   FILEINFO* new = get_temp_file(NBUFF, Buffer->filename);
  118.   new->parent = Buffer->parent;    /* When new ends read from infile */
  119.   Buffer->parent = new;            /* When Buffer dries up, read from new */
  120.   *Buffer->bptr = EOS;
  121.   Buffer->bptr = Buffer->buffer;
  122.   return new;
  123. }
  124.  
  125. /* Helper function for the getchar macro used by internal defmacros */
  126. char
  127. NextMacInChar() {
  128.   char c;
  129.   do {
  130.     if (MacInFile->parent == NULL) {
  131.       return(EOF);              /* End of file */
  132.     } else {
  133.       FILEINFO* new = MacInFile->parent;
  134.       free(MacInFile->filename);      /* Free name and    */
  135.       if (MacInFile->progname != NULL)      /* if a #line was seen, */
  136.     free(MacInFile->progname);      /* free it, too.    */
  137.       free(MacInFile->buffer);          /* Free buffer */
  138.       free((char *) MacInFile);          /* Free file space    */
  139.  
  140.       *new->bptr = EOS;
  141.       new->bptr = new->buffer;
  142.       c = *new->bptr++;
  143.       MacInFile = new;
  144.     }
  145.   }
  146.   while (c == EOS);
  147.   return (c);
  148. }
  149.  
  150. void
  151. NextMacOutBuffer()
  152. {
  153.   MacOutFile = next_buffer(MacOutFile);
  154.   MacOutEnd = MacOutFile->buffer + (NBUFF - 1);
  155. }
  156.  
  157. void
  158. NextMacOutString(s)
  159. char* s;
  160.   int slen = strlen(s);
  161.   int blen;
  162.   while ((blen = (MacOutEnd - MacOutFile->bptr) - 1) < slen) {
  163.     if(blen>0) {
  164.       strncpy(MacOutFile->bptr, s, blen);
  165.       MacOutFile->bptr += blen;
  166.       s += blen;
  167.       slen -= blen;
  168.     }
  169.     NextMacOutBuffer();
  170.   }
  171.   strcpy(MacOutFile->bptr, s);
  172.   MacOutFile->bptr += slen;
  173. }
  174.  
  175. static char* new_buffer (obuf, obufend, file)
  176.      char*      obuf;            /* Output Buffer */
  177.      char**     obufend;        /* End of Output Buffer */
  178.      FILEINFO    **file;            /* Funny #include    */
  179. {
  180.   FILEINFO* old = *file;
  181.   FILEINFO* new = get_temp_file(NBUFF, old->filename);
  182.   *obuf = EOS;
  183.   /* NOTE: this code leaves file->bptr pointing at the END of the */
  184.   /*       buffer.  This is to keep double_copy from having to use */
  185.   /*       strlen to get the number of bytes in the buffer */
  186.   old->bptr = obuf;
  187.   old->parent = new;          /* When file dries up, read from new */
  188.   *obufend = new->buffer + (NBUFF - 1);
  189.   *file = new;
  190.   return new->bptr;
  191. }
  192. /*
  193.  * Copy from the input stream to a buffer, stopping at the first
  194.  * delim, but including everything with matching {} [] ()
  195.  * <> "" '' and comments found along the way.
  196.  */
  197. static FILEINFO*
  198. copy_body(file, delim, is_string, is_top_level, expand)
  199.      FILEINFO    *file;            /* Funny #include    */
  200.      char delim;            /* Char to stop on */
  201.      int is_string;            /* True when inside a string */
  202.      int is_top_level;            /* True when the outer most call */
  203.      int expand;            /* if non-zero, macroexpand */
  204. {
  205.   register char* obuf;            /* Current output pointer */
  206.   register char c;            /* Current character */
  207.   char p;                /* Previous character */
  208.   char* obufend;            /* End of output buffer */
  209.   char newdelim;            /* New delimiter to look for */
  210.   int new_is_string = FALSE;        /* New is_string flag */
  211.  
  212.   obuf = file->bptr;            /* -> output buffer    */
  213.   obufend = file->buffer + (NBUFF - 1);    /* Note its end        */
  214.  
  215.   *obuf = EOS;
  216.   for (p = EOS; (c = get()) != EOF_CHAR; p = c) {
  217.     if (obuf >= obufend)        /* End of buffer, get a new one */
  218.       obuf = new_buffer(obuf, &obufend, &file);
  219.     if(expand && !is_string && type[c] == LET) {    /* expand macros */
  220.       c = macroid(c);            /* Grab the token    */
  221.       if (obuf >= obufend-strlen(tokenbuf)) /* End of buffer, get a new one */
  222.     obuf = new_buffer(obuf, &obufend, &file);
  223.       if(type[c] == LET) {
  224.     strcpy(obuf, tokenbuf);
  225.     obuf += strlen(tokenbuf);
  226.     continue;
  227.       }
  228.     }
  229.     *obuf++ = c;
  230.     if (c == delim)            /* Quit when delimeter found */
  231.       if (delim != '/' || p == '*') {    /* Special test for end of comment */
  232.     *obuf = EOS;
  233.     file->bptr = obuf;
  234.     return(file);
  235.       }
  236.     if (!is_string) {
  237.       switch (c) {           /* Look for new delimeters when not is_string */
  238.       case DEF_MAGIC:
  239.       case TOK_SEP:
  240.       case COM_SEP:
  241.     obuf--; continue;          /* Ignore magic characters */
  242.       case '{':  newdelim = '}'; break;
  243.       case '[':  newdelim = ']'; break;
  244.       case '(':  newdelim = ')'; break;
  245.       case '<':
  246.     if (delim != '>') continue;
  247.     newdelim = '>'; break;
  248.       case '\'':  newdelim = '\''; new_is_string = TRUE; break;
  249.       case '\"':  newdelim = '\"'; new_is_string = TRUE; break;
  250.       case '*':                  /* Start of C comment? */
  251.     if (p == '/') {
  252.       if (keepcomments) {
  253.         newdelim = '/';
  254.         new_is_string = TRUE;
  255.         break;
  256.       }                  /* Eat comment */
  257.       for (; (c = get()) != EOF_CHAR; p = c) {
  258.         if (c == '\n') {
  259.           if (obuf >= obufend)      /* End of buffer, get a new one */
  260.         obuf = new_buffer(obuf, &obufend, &file);
  261.           *obuf++ = c;          /* Preserve newlines */
  262.         } else if (c == '/' && p == '*') break;
  263.       }
  264.       if (obuf+2 >= obufend)      /* End of buffer, get a new one */
  265.         obuf = new_buffer(obuf, &obufend, &file);
  266.       *obuf++ = '*';          /* Terminate the comment */
  267.       *obuf++ = '/';
  268.     }
  269.     continue;
  270.  
  271.       case '/':                /* Start of C++ comment? */
  272.     if (p == '/') {
  273.       if (keepcomments) {
  274.         newdelim = '\n';
  275.         new_is_string = TRUE;
  276.         break;
  277.       }                  /* Eat comment */
  278.       while ((c = get()) != EOF_CHAR && c != '\n');
  279.       if (obuf >= obufend)      /* End of buffer, get a new one */
  280.         obuf = new_buffer(obuf, &obufend, &file);
  281.       *obuf++ = c;          /* Preserve newlines */
  282.     }
  283.     continue;
  284.  
  285.       default:
  286.       continue;    /* Loop to next character, unless break above */
  287.       }    /* end switch */
  288.       if (is_top_level && newdelim == delim) {
  289.     is_top_level = FALSE;
  290.     continue;        /* at top_level, with left side of delimiter */
  291.       }
  292.       file->bptr = obuf;
  293.       file = copy_body(file, newdelim, new_is_string, FALSE, expand);
  294.       if (file == NULL)
  295.     return(NULL);
  296.       obuf = file->bptr;        /* -> output buffer    */
  297.       obufend = file->buffer + (NBUFF - 1); /* Note its end        */
  298.       new_is_string = FALSE;
  299.       c = EOS; /* don't know what last character is anymore */
  300.     }  /* if !is_string */
  301.   } /* end while */
  302.   fprintf(stderr, "Searching for '%c'\n", delim); /* DEBUG */
  303.   infile = getfile(1, current_file);      /* Hack up infile */
  304.   infile->fp = stdin;              /* to print correct file */
  305.   line = current_line;              /* and line number */
  306.   cfatal("End of file during %s macro expansion.", file->filename);
  307.   return(NULL);
  308. } /* copy_body */
  309.  
  310. #if !(HOST == SYS_OS2 || HOST == SYS_VMS || HOST == SYS_MVS)
  311. /*
  312.  *  support for fork and pipe on unix machines only
  313.  */
  314.  
  315. /*
  316.  * Copy two files in parallel: from in1 to out2 and in2 to out2 
  317.  * Returns when both copies are complete, closing all files.
  318.  * in1 and out2 will not block (i.e. they're files)
  319.  * out1 and in2 may be pipes and can block for long periods
  320.  */
  321. static void
  322. doublecopy (in1, out1, in2, out2)
  323.  
  324.   FILEINFO    *in1, *out2;  
  325.   int           out1, in2;
  326. {
  327.   fd_set readfds;
  328.   fd_set writefds;
  329.   fd_set errorfds;
  330.   int n, width;
  331.   int is_first = TRUE;
  332.   char* outptr = in1->buffer;
  333.  
  334.   width = 1 + ((in2 > out1) ? in2 : out1); /* width = max(in2, out1); */
  335.  
  336.   do {
  337.     FD_ZERO(&readfds);
  338.     FD_ZERO(&writefds);
  339.     FD_ZERO(&errorfds);
  340.     if (in2  >= 0) FD_SET(in2, &readfds);
  341.     if (out1 >= 0) FD_SET(out1, &writefds);
  342.  
  343.     if (select(width, &readfds, &writefds, &errorfds, NULL) <= 0)
  344.       perror("Select Error");
  345.  
  346.     if (out1>=0 && FD_ISSET(out1, &errorfds))
  347.       perror("Select output Error");
  348.  
  349.     if (in2>=0 && FD_ISSET(in2, &errorfds))
  350.       perror("Select input Error");
  351.  
  352.     if (!(out1>=0 && FD_ISSET(out1, &writefds)) &&
  353.     !(in2>=0 && FD_ISSET(in2, &readfds))) {
  354.       perror("Select when nothing happened");
  355.     }
  356.  
  357.     if (out1>=0 && FD_ISSET(out1, &writefds)) {
  358.       if (in1 == NULL) {
  359.     close(out1);
  360.     out1 = -1;
  361.       } else {
  362.     int nbytes = in1->bptr - outptr;
  363.     int wbytes = write(out1, outptr, nbytes);
  364.     if (wbytes < 0)
  365.       perror("Error writing to pipe");
  366.     outptr += wbytes;
  367.     if (nbytes == wbytes) {
  368.       in1 = in1->parent;
  369.       if (in1 != NULL)
  370.         outptr = in1->buffer;
  371.     }
  372.       }
  373.     }
  374.  
  375.     if (in2>=0 && FD_ISSET(in2, &readfds)) {
  376.       if (is_first)
  377.     is_first = FALSE;
  378.       else {                /* Chain in a new buffer */
  379.     FILEINFO* new = get_temp_file(NBUFF, out2->filename);
  380.     new->parent = out2->parent;    /* When new ends read from infile */
  381.     out2->parent = new;            /* When out2 dries up, read from new */
  382.     out2 = new;
  383.       }
  384.       n = read(in2, out2->buffer, NBUFF);
  385.       if (n <= 0) {
  386.     if(n<0)
  387.       perror("Error reading from pipe");
  388.     close(in2);
  389.     in2 = -1;
  390.       } else {
  391.     char* p = out2->buffer + n;
  392.     *p = EOS;    
  393.       }
  394.     }
  395.   } while (in2>=0 || out1>=0);
  396. } /* end doublecopy */
  397.  
  398. /*
  399.  * Fork an external macro expander
  400.  */
  401. static void
  402. fork_macro (args, ifile, ofile)
  403.      struct macroargs* args;
  404.      FILEINFO *ifile, *ofile;
  405. {     
  406.   int inchannel, outchannel;
  407.   int forkin, forkout;
  408.   int pid, status;
  409.   {                    /* Create the pipes */
  410.     int sv[2];
  411.     pipe (sv);
  412.     forkin = sv[0];
  413.     outchannel = sv[1];
  414.     pipe (sv);
  415.     inchannel = sv[0];
  416.     forkout = sv[1];
  417.   }
  418.                     /* Fork the macro process */
  419.   if ((pid = fork ()) == 0) {
  420.     /* child process.  Move pipes to stdin and stdout then exec */
  421.     close(0); dup2(forkin, 0);
  422.     close(1); dup2(forkout, 1);
  423.     close(forkin);
  424.     close(forkout);
  425.     close(inchannel);  /* Close files used only by the parent */
  426.     close(outchannel);
  427.     execv(args->program, args->args);
  428.     /* execv never returns */
  429.   }
  430.   close(forkin);  /* Close files used only by the child */
  431.   close(forkout);
  432.   if (pid < 0) 
  433.     cfatal("Can't fork %s macro.", args->program);
  434.   /* Make output to the pipe non-blocking to avoid deadlocks */
  435.   fcntl(outchannel, F_SETFL, FNDELAY | fcntl(outchannel, F_GETFL, 0));
  436.   doublecopy (ifile, outchannel, inchannel, ofile);
  437.   wait(&status);
  438.   if(status != 0)
  439.     cerror("Error during %s macro expansion", args->program);
  440. } /* end fork_macro */
  441.  
  442. #else
  443. /* 
  444.  * execute macro using temp files to redirect stdin and stdout
  445.  * temp file implementation used for DOS, VMS and MVS ports
  446.  * instead of fork/pipe implementation
  447.  */
  448. void run_macro (macname, args, in, out)
  449.     char* macname;
  450.     char* args[];
  451.     char *in, *out;
  452. {
  453.     int status;
  454.     int ifd, ofd;
  455. #if HOST != SYS_OS2 && HOST != SYS_MVS
  456.     /* 
  457.      * no fork function on OS2 or MVS
  458.      */
  459.     int pid;
  460.     if ((pid=fork()) == 0) {
  461.       /* child process */
  462.       ifd = open(in, O_RDONLY);
  463.       if (ifd < 0) {
  464.     perror("cpp exec macro error");
  465.     cerror("Cannot open external macro temp file \"%s\"", in);
  466.     return;
  467.       }
  468.       ofd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  469.       if (ofd < 0) {
  470.     perror("cpp exec macro error");
  471.     cerror("Cannot open external macro temp file \"%s\"", out);
  472.     return;
  473.       }
  474.       if (dup2(ifd, 0) < 0) {
  475.     perror("cpp exec macro error");
  476.     cerror("Cannot dup external macro temp file \"%s\"", in);
  477.     return;
  478.       }
  479.       if (dup2(ofd, 1) < 0) {
  480.     perror("cpp exec macro error");
  481.     cerror("Cannot dup external macro temp file \"%s\"", out);
  482.     return;
  483.       }
  484.       execvp(macname, args);
  485.       perror("cpp exec macro error");
  486.       cerror(stderr, "cpp: Cannot execute %s\n", macname);
  487.       close(ifd);
  488.       close(ofd);
  489.     }
  490.     if (pid < 0)
  491.       cfatal("Cannot execute %s\n", macname);
  492.     wait(&status);
  493. #else
  494.     /* for OS/2 and MVS
  495.      * use system function to execute macro with command interpreter
  496.      */
  497.     char arglist[256];
  498.     char subarg[256];
  499.     char command[512];
  500.     char* c;
  501.     /* build arglist of macro command */
  502.     char** argp = &args[1];          /* skip macro name */
  503.     if (*argp != NULL) {
  504.       sprintf(arglist,"%s", *argp);
  505.       for (++argp; *argp !=NULL; argp++) {
  506.     strcpy(subarg,arglist);
  507.     sprintf(arglist,"%s %s",subarg, *argp);
  508.       }
  509.     } else strcpy(arglist,"");
  510.     for (c = macname; *c != '\n'; c++)
  511.       if (*c == '/') *c = '\\';
  512.     /* build complete command redirecting stdin and stdout to temp files */
  513.     sprintf(command,"%s %s < %s > %s",macname,arglist,in,out);
  514.     /* execute command */
  515.     status = system(command);
  516. #endif
  517.     if(status != 0)
  518.       cerror("Error during %s macro expansion", macname);
  519. } /* end of run_macro */
  520.  
  521. #if HOST == SYS_OS2 || HOST == SYS_MVS
  522. static char* TMPDIR = "E:\\";
  523. #else
  524. static char* TMPDIR = "/tmp/";
  525. #endif
  526.  
  527. /* exec macro using temp files to redirect stdin and stdout */
  528. void exec_macro (args, ifile, ofile)
  529.      struct macroargs* args;
  530.      FILEINFO *ifile, *ofile;
  531. {     
  532.   char tmpin[256];
  533.   char tmpout[256];
  534.   FILEINFO *f;
  535.   int n, ifds,ofds;
  536.   char* p;
  537.  
  538.   /* make temp file names using process id */
  539.   sprintf(tmpin, "%s%s%d", TMPDIR, "in", getpid() % 9999);
  540.   sprintf(tmpout, "%s%s%d", TMPDIR, "out", getpid() % 9999);
  541.  
  542.   /* copy ifile to input temp file and close it */
  543.   ifds = open(tmpin, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  544.   if (ifds < 0) {
  545.     perror("cpp exec macro error");
  546.     cerror("Cannot open external macro temp file \"%s\"", tmpin);
  547.     return;
  548.   }
  549.   f = ifile;
  550.   for (;;) {
  551.     n = write(ifds, f->buffer, f->bptr - f->buffer);
  552.     if (n < 0) {
  553.       perror("cpp macro error");
  554.       cerror("Cannot write to external macro temp file \"%s\"", tmpin);
  555.       close(ifds);
  556.       return;
  557.     }
  558.     if ((f = f->parent) == NULL) break;
  559.   }
  560.   close(ifds);
  561.  
  562.   /*  fork and exec external macro */
  563.   run_macro(args->program, args->args, tmpin, tmpout);
  564.  
  565.   /* copy output temp file to ofile and close it */
  566.   ofds = open(tmpout, O_RDONLY);
  567.   if (ofds < 0) return;
  568.   n = read(ofds, ofile->buffer, NBUFF);
  569.   for (;;) {
  570.     FILEINFO* new;
  571.     if (n <= 0) break;
  572.     p = ofile->buffer + n;
  573.     *p = EOS;
  574.     new = get_temp_file(NBUFF, ofile->filename);
  575.     new->parent = ofile->parent;
  576.     ofile->parent = new;
  577.     ofile = new;
  578.     n = read(ofds, ofile->bptr, NBUFF);
  579.   }
  580.   close(ofds);
  581.  
  582.   /* remove temp files */
  583.   unlink(tmpin);
  584.   unlink(tmpout);
  585. }
  586.  
  587. #endif  /* for DOS or VMS or MVS */
  588.  
  589. /*
  590.  * Call an internal macro expander
  591.  */
  592. static void
  593. call_expander (args, ifile, ofile)
  594.      struct macroargs* args;
  595.      FILEINFO *ifile, *ofile;
  596. {     
  597.   int nargs = 0;
  598.   char** arg = args->args;
  599.   while(*arg++ != NULL) nargs++;    /* Count args */
  600.   *ifile->bptr = EOS;
  601.   ifile->bptr = ifile->buffer;
  602.   MacInFile = ifile;            /* Set global I/O buffers */
  603.   MacOutFile = ofile;
  604.   MacOutEnd = ofile->buffer + (NBUFF - 1);
  605.   if (((*args->expander)(nargs, args->args)) != 0)
  606.     cerror("Error during internal %s macro expansion", args->program);
  607.   *MacOutFile->bptr = EOS;
  608.   MacOutFile->bptr = MacOutFile->buffer;
  609. }
  610. /*
  611.  * Expand an external macro.
  612.  *   1. Grab the macro name and everything until the next semicolon
  613.  *      (includling all matching {} [] () <> "" '' and comments found along
  614.  *      the way), and put it in a macro buffer.  Commands found along
  615.  *      the way are processed.
  616.  *   2. Pipe this into the macro, and put the result in another macro buffer.
  617.  *   3. Process the macro expansion.
  618.  */
  619. void
  620. expand_external(dp)
  621.   char* dp;
  622. {
  623.   FILEINFO    *ifile, *ofile;    /* The first output file buffer */
  624.   struct macroargs* args = (struct macroargs*) dp; /* macro arguments */
  625.   char* name = args->name;        /* Macro name */
  626.   extern int macroid();            /* Get a macro-expanded character */
  627.  
  628.   if(!args->recursive) {
  629.     FILEINFO *f;            /* If recursion where it isn't allowed */
  630.     for (f = infile; (f->parent != NULL); f = f->parent) {
  631.       if(strcmp(f->filename, name) == 0) {
  632.     fputs(name, stdout);    /* Skip expansion */
  633.     return;}}}
  634.   /*
  635.    * Ensure the start is correct
  636.    */
  637.   strcpy(work, name);
  638.   workp = work + strlen(name);
  639.   { char c;            /* current character */
  640.     char d = args->conditional;
  641.     if(d != EOS) {        /* check for conditional character */
  642.       instring = TRUE;
  643.       while(isspace(c = get())) *workp++ = c;
  644.       *workp = EOS;
  645.       instring = FALSE;
  646.       unget();
  647.       if (c != d) {        /* When no conditional character, exit */
  648. #ifdef paranoia
  649.     cwarn("defmacro \"%s\" needs arguments", name);
  650. #endif
  651.                 /* Place macro name back in input stream */
  652.                 /* being careful to avoid recursion */
  653.     /* fputs(work, stdout); this isn't good enough */
  654.     ofile = get_temp_file(strlen(work)+1, name);
  655.     ofile->parent = infile;
  656.     ofile->buffer[0] = DEF_MAGIC;
  657.     strcpy(ofile->buffer+1, work);
  658.     infile = ofile;
  659.     return;
  660.       }
  661.     }
  662.   }
  663.   current_line = line;
  664.   strcpy(current_file,
  665.      (infile->progname != NULL) ? infile->progname : infile->filename);
  666.   /*
  667.    * Copy everything to the next ;
  668.    */
  669.   ifile = get_temp_file(NBUFF, name);
  670.   ofile = get_temp_file(NBUFF, name);
  671.   strcpy(ifile->buffer, work);    /* Send name to macro expander */
  672.   ifile->bptr = ifile->buffer + strlen(work);
  673.   instring = TRUE;        /* Ensure comments get copied too */
  674.   if (copy_body(ifile, args->delimiter, FALSE, TRUE, args->expanding) == NULL) {
  675.     instring = FALSE;
  676.     return;
  677.   }
  678.   instring = FALSE;
  679.   infile->line = line;
  680.   /*
  681.    * Expand the macro
  682.    */
  683.   ofile->parent = infile;
  684.   if (args->expander == NULL) {
  685. #if !(HOST == SYS_OS2 || HOST == SYS_VMS || HOST == SYS_MVS)
  686.     fork_macro(args, ifile, ofile);
  687. #else
  688.     exec_macro(args, ifile, ofile);
  689. #endif
  690.   }
  691.   else
  692.     call_expander(args, ifile, ofile);
  693.   infile = ofile;
  694. }
  695.  
  696. /*
  697.  * scanthing(delim)
  698.  *   Copy from input to work, stopping at delim
  699.  *   but including everything with matching {} [] ()
  700.  *   <> "" '' and comments found along the way.
  701.  */
  702. static int
  703. scanthing(delim, is_string, is_top_level)
  704.   char delim;                     /* Current delimiter */
  705.   int is_string, is_top_level;            /* flags */
  706. {
  707.   char c;                     /* Current character */
  708.   char newdelim;            /* New delimiter to look for */
  709.   int new_is_string = FALSE;        /* New is_string flag */
  710.  
  711.   if (is_top_level) {            /* Skip leading whitespace */
  712.     workp = work;
  713.     c = skipws();
  714.     unget();
  715.   }
  716.   while ((c = get()) != EOF_CHAR) {
  717.     save(c);
  718.     if (delim == c &&            /* Quit when delimeter found */
  719.     delim != ' ') {
  720.       *workp = EOS;
  721.       return FALSE;
  722.     }
  723.     if (!is_string) {
  724.       switch (c) {        /* Look for new delimeters when not is_string */
  725.       case ' ':
  726.       case ',':
  727.       case '\t':
  728.       case '\n':
  729.     if (delim == ' ') {
  730.       unget();
  731.       workp -= 1;
  732.       *workp = EOS;
  733.       return FALSE;
  734.     } else continue;
  735.       case '{':  newdelim = '}'; break;
  736.       case '[':  newdelim = ']'; break;
  737.       case '(':  newdelim = ')'; break;
  738.       case '\'':  newdelim = '\''; new_is_string = TRUE; break;
  739.       case '\"':  newdelim = '\"'; new_is_string = TRUE; break;
  740.       default: 
  741.     continue;        /* Loop to next character, unless break above */
  742.       }    /* end switch */
  743.       if (is_top_level && newdelim == delim) {
  744.     is_top_level = FALSE;
  745.     continue;        /* at top_level, with left side of delimiter */
  746.       }
  747.       scanthing(newdelim, new_is_string, FALSE);
  748.       new_is_string = FALSE;
  749.     }  /* if !is_string */
  750.   } /* end while */
  751.   return (c == EOF_CHAR);
  752. /*
  753.  * create a new defmacro (internal function)
  754.  */
  755. void
  756. new_defmacro(name, expanding, recursive, delimiter, conditional, 
  757.          expander, program, args)
  758.   char* name;
  759.   char  expanding;        /* Non-zero when args are macro expanded */
  760.   char  recursive;        /* Non-zero when recursive */
  761.   char  delimiter;        /* Delimiter */
  762.   char  conditional;        /* Expand only if this char found after name */
  763.   internal_expander expander;    /* Macro expander or NULL if file */
  764.   char *program;        /* macro program pathname */
  765.   char** args;            /* arg list (vector of strings) */
  766. {
  767.   struct macroargs* new;
  768.   int nargs = 1;
  769.   char** argp;
  770.  
  771.   if (args == NULL) {              /* Setup default args */
  772.     args = (char**) getmem(sizeof(char*)*2);
  773.     args[0] = program;
  774.     args[1] = NULL;
  775.   }
  776.   for(argp = args; *argp++!=NULL; nargs++);    /* Count arguments */
  777.  
  778.   new = (struct macroargs*) getmem(sizeof(struct macroargs) +
  779.                    (nargs*sizeof(char*)));
  780.   new->name = name;
  781.   new->expanding = expanding;
  782.   new->recursive = recursive;
  783.   new->delimiter = delimiter;
  784.   new->conditional = conditional;
  785.   new->expander = expander;
  786.   new->program = program;
  787.   while(nargs-- > 0)
  788.     new->args[nargs] = args[nargs];
  789.   define_builtin(name, expand_external, (char*) new);
  790. }
  791.  
  792. /*
  793.  * Define an external macro handler
  794.  *
  795.  *     Called when #pragma defmacro
  796.  *     is found in the input.
  797.  * The complete form is:
  798.  * #pragma defmacro name <file> options
  799.  * #pragma defmacro name "file" options
  800.  * #pragma defmacro name program options
  801.  *
  802.  * where "options" is zero or more of the following:
  803.  *  recursive    - when present, the macro is recursively expanded
  804.  *  delimiter=?  - the default delimiter of ; is replaced with ?
  805.  *  condition=?  - Expand only if this char found after name
  806.  *  other        - other options are passed as arguments to the
  807.  *                 macro expander
  808.  */
  809. void
  810. define_external()
  811. {
  812.   char c;
  813.   char delim;
  814.   char* name;
  815.   char* program;
  816.   int nargs = 1;
  817.   int maxargs = 32;
  818.   char* args[32];
  819.   int recursive = 0;
  820.   int expanding = 0;
  821.   int delimiter = ';';
  822.   int conditional = EOS;
  823.   internal_expander expander = NULL;
  824.  
  825.   c = skipws();
  826.   scanid(c);
  827.   name = savestring(tokenbuf);
  828.   program = name;
  829.         /* Parse_include returns EOS for internal macros (no " or <) */
  830.   delim = parse_include();          /* Get program to work */
  831.   strcpy(tokenbuf, work); /* Keep copy because findinclude mangles it */
  832. #if HOST == SYS_OS2
  833.   strcat(work,".exe");
  834. #endif
  835.   if (delim == EOS || findinclude(work, (delim == '"'), X_OK) == FALSE) {
  836.     struct expander_pair* p = internal_macros; /* Macro file not found */
  837.     strcpy(work, tokenbuf);                    /* restore progam name */
  838.     for (; p->name != NULL; p++)           /* Search for internal macro */
  839.       if (strcmp(p->name, work) == 0) {
  840.     expander = p->function;
  841.     goto found_internal;
  842.       }    
  843.     cerror("Cannot open macro file \"%s\"", work);
  844.     goto macerr;
  845.   }
  846. /*
  847. #if HOST == SYS_VMS || HOST == SYS_MVS
  848.   else {
  849.     cerror("External macros not supported for \"%s\"", work);
  850.     goto macerr;
  851.   }
  852. #endif
  853. */
  854. found_internal:
  855.   program = args[0] = savestring(work);
  856.  
  857.   c = skipws();
  858.   while (c != '\n' && c != EOF_CHAR) {
  859.     while (c == ',') {
  860.       args[nargs++] = ",";
  861.       c = skipws();
  862.     }
  863.     unget();
  864.     if (scanthing(' ', FALSE, TRUE))
  865.       cfatal("End of file during #pragma defmacro %s processing.", name);
  866.     if(strcmp(work, "recursive") == 0) {
  867.       recursive = 1;
  868.       c = skipws();
  869.       continue;
  870.     }
  871.     if(strcmp(work, "expanding") == 0) {
  872.       expanding = 1;
  873.       continue;
  874.     } 
  875.     {
  876.       int* option = NULL;
  877.       if(strncmp(work, "delimiter", 9) == 0) option = &delimiter;
  878.       if(strncmp(work, "condition", 9) == 0) option = &conditional;
  879.       if(option != NULL) {
  880.     if (work[9] != '=' && (c = skipws()) != '=') {
  881.       cerror("In #pragma defmacro, Missing = after %s", work);
  882.       goto macerr;
  883.     }
  884.     if(strlen(work) == 11)
  885.       *option = work[10];
  886.     else 
  887.       *option = skipws();
  888.     if(*option != '\n') c = skipws();
  889.     continue;
  890.       }
  891.     }                /* must be a program option */
  892.     if (nargs >= maxargs) {
  893.       cerror("In #pragma defmacro %s, too many arguments.", name);
  894.       goto macerr;
  895.     }
  896.     args[nargs] = savestring(work);
  897.     nargs++;
  898.     c = skipws();
  899.   } /* end while not newline */
  900.   unget();            /* Force nl after includee    */
  901.   args[nargs] = NULL;
  902.   new_defmacro(name, expanding, recursive, delimiter, conditional, 
  903.            expander, program, args);
  904.   return;
  905.  
  906.  macerr:
  907.   skipnl();            /* Skip to end of line */
  908.   unget();            /* Force nl */
  909.   return;
  910. }
  911.  
  912. /*-----------------------------------------------------------------------------
  913.  * Process DEFPACKAGE command.
  914.  * format is: defpackage NAME <path> options
  915.  * where options are optional and seperated by comma's.
  916.  *
  917.  * This really should be an internal defmacro, but
  918.  * it was so much easier to handle it here...
  919.  */
  920. static char* defpackage_options[] = {
  921.   "length", "case",
  922.   "start", "increment", "template", "max", "use_first", "nospace", NULL};
  923.  
  924. void
  925. define_package(dp)
  926.   char* dp;
  927. {
  928.   extern void package_define();
  929.   char c;
  930.   char delim;
  931.   char* pkgname;
  932.   char* pkgfile;
  933.   char* options[8];
  934.   long loptions[6];
  935.   int np;
  936.   char errmsg[100];
  937.  
  938.   options[0]=""; options[1]=""; options[2]=""; options[3]="";
  939.   options[4]=""; options[5]=""; options[6]=""; options[7]="";
  940.   c = skipws();
  941.   scanid(c);
  942.   pkgname = savestring(tokenbuf);
  943.   if((delim = parse_include()) == EOS) {
  944.     cerror("DEFPACKAGE syntax error", NULLST);
  945.     goto macerr;
  946.   }
  947.   if (findinclude(work, (delim == '"'), R_OK) == FALSE) {
  948.     cerror("Cannot open package header file \"%s\"", work);
  949.     goto macerr;
  950.   }
  951.   pkgfile = savestring(work);
  952.                       /* Get options */
  953.   while ((c=skipws()) != '\n' && c != EOF_CHAR) {
  954.     char** p;
  955.     if (c == ',')              /* Skip over commas */
  956.       if ((c=skipws()) == '\n' || c == EOF_CHAR)
  957.     break;
  958.     scanid(c);
  959.     np = 0;
  960.     p = defpackage_options;
  961.     for (; *p != NULL; p++, np++) 
  962.       if(strcmp(tokenbuf, *p) == 0) break;
  963.     if (*p == NULL) {
  964.       sprintf(errmsg, "In DEFPACKAGE %s, unknown option \"%s\"",
  965.           pkgname, tokenbuf);
  966.       cerror(errmsg, NULLST);
  967.       goto macerr;
  968.     } else if ((c = skipws()) != '=') {
  969.       sprintf(errmsg, "In DEFPACKAGE %s, Missing = after %s option",
  970.           pkgname, tokenbuf);
  971.       cerror(errmsg, NULLST);
  972.       goto macerr;
  973.     } else {
  974.       c = skipws();
  975.       scanid(c);
  976.       options[np] = savestring(tokenbuf);
  977.     }
  978.   }
  979.   unget();  /* Force nl after includee */
  980.                       /* Convert numeric options */
  981.   for(np=2; np<8; np++) {
  982.     char* endp[1];
  983.     long val = strtol(options[np], endp, 0);
  984.     if(**endp != EOS) {
  985.       sprintf(errmsg, "In DEFPACKAGE %s, Illegal value %s for %s option",
  986.           pkgname, options[np], defpackage_options[np]);
  987.       cwarn(errmsg, NULLST);
  988.     }
  989.     loptions[np-2] = val;
  990.   }
  991.   package_define(pkgname, pkgfile, 
  992.          options[0], options[1], loptions[0], loptions[1],
  993.          loptions[2], loptions[3], loptions[4], loptions[5]);
  994.   return;
  995.  
  996.  macerr:
  997.   skipnl();            /* Skip to end of line */
  998.   unget();            /* Force nl */
  999.   return;
  1000. }
  1001.